# Go语言方法

# 1 方法的定义

Go语言中的方法(Method)是一种作用于特定类型变量的函数。方法是与对象实例绑定的特殊函数。 这种特定类型变量叫做接收者(Receiver)。接收者的概念就类似于其他语言中的this或者 self

方法定义语法

func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
    //方法体
}
1
2
3
  • 接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名的第一个小写字母,而不是self、this之类的命名。
  • 接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型。
  • 方法名、参数列表、返回参数:具体格式与函数定义相同。

举例如下

package main

import "fmt"

type student struct {
	name string
	age  int
}
type dog struct {
	name string
}
func (s *student) say(){
	fmt.Printf("%s向大家问好\n", s.name)
}
func main() {
	//声明一个变量stu
	var stu student
	//变量赋值
	stu.name="福小林"
	//调用student类型特定的方法say()
	stu.say()
	
/*  var d dog
	d.name="牧羊犬"
	//此处不能调用say(),报错d.say undefined (type dog has no field or method say)
	d.say() */
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 1.1 指针类型的接收者

指针类型的接收者由一个结构体的指针组成,由于指针的特性,调用方法时修改接收者指针的任意成员变量,在方法结束后,修改都是有效的。这种方式就十分接近于其他语言中面向对象中的this或者self。 例如我们为Person添加一个SetAge方法,来修改实例变量的年龄。

//定义一个student的结构体
type student struct {
	name string
	age  int
}
func (s *student) say(){
	s.age++

}
func main() {
	//声明一个变量stu
	var stu student
	//变量赋值
	stu.name="张三"
	stu.age=18
	fmt.Printf("调用say()方法之前%s的年龄%d\n",stu.name,stu.age)  //18
	//调用student类型特定的方法say()
	stu.say()
	fmt.Printf("调用say()方法后%s的年龄%d\n",stu.name,stu.age)  //19
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 1.2 值类型的接收者

当方法作用于值类型接收者时,Go语言会在代码运行时将接收者的值复制一份。在值类型接收者的方法中可以获取接收者的成员值,但修改操作只是针对副本,无法修改接收者变量本身。


type student struct {
	name string
	age  int
}
func (s student) say(){
	s.age++

}
func main() {
	//声明一个变量stu
	var stu student
	//变量赋值
	stu.name="李四"
	stu.age=30
	fmt.Printf("调用say()方法之前%s的年龄%d\n",stu.name,stu.age) //30
	//调用student类型特定的方法say()
	stu.say()
	fmt.Printf("调用say()方法后%s的年龄%d\n",stu.name,stu.age) //30
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 1.3 什么时候使用指针类型接收者

  • 需要修改接收者中的值
  • 接收者是拷贝代价比较大的大对象
  • 保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。

# 2 为什么有函数了还需要方法呢

原因

  • Go不是纯粹的面向对象编程语言,而且Go不支持类.因此基于类型的方法是一种实现和类相似行为的途径
  • 相同的名字的方法可以定义在不同的类型上,而相同名字的函数是不被允许的。

# 3 任意类型添加方法

在Go语言中,接收者的类型可以是任何类型,不仅仅是结构体,任何类型都可以拥有方法。 举个例子,我们基于内置的int类型使用type关键字可以定义新的自定义类型,然后为我们的自定义类型添加方法

//MyInt 将int定义为自定义MyInt类型
type MyInt int

//SayHello 为MyInt添加一个SayHello的方法
func (m MyInt) SayHello() {
	fmt.Println("Hello, 我是一个int。")
}
func main() {
	var m1 MyInt
	m1.SayHello() //Hello, 我是一个int。
	m1 = 100
	fmt.Printf("%#v  %T\n", m1, m1) //100  main.MyInt
}
1
2
3
4
5
6
7
8
9
10
11
12
13

注意:非本地类型不能定义方法,也就是说我们不能给别的包的类型定义方法。

# 4 结构体的“继承”

Go语言中使用结构体也可以模拟实现其他编程语言中面向对象的继承。

package main

import "fmt"

//Animal 动物
type Animal struct {
	name string
}

func (a Animal) move() {
	fmt.Printf("%s会动!\n", a.name)
}

//Dog 狗  Dog包裹了Animal 模拟继承了Animal  
type Dog struct {
	Feet    int8
	Animal //Animal拥有的方法,Dog也拥有了,因为Dog包裹了Animal,通过嵌套匿名结构体模拟实现继承
}

func (d Dog) wang() {
	fmt.Printf("%s会汪汪汪~\n", d.name) // d.name相当于 d.Animal.name
}

func main() {
	d1 := Dog{
		Feet: 4,
		Animal: Animal{
			name: "旺财",
		},
	}
	d1.wang() //旺财会汪汪汪~
	d1.move() //旺财会动!
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 函数和方法的区别

函数和方法的区别

1、普通函数:接收者(函数参数)为值类型时,不能将指针类型的数据直接传递,反之亦然。

func function_name([parameter list]) [return_types] {
   /*函数体*/
}
1
2
3

2、方法(如struct方法): 接收者为值类型时,可以直接用指针类型的变量调用方法,反之亦然。

func (variable_name variable_data_type) function_name() [return_type]{
   /* 方法体*/
}
1
2
3

TIP

  • 方法与函数的区别是,函数不属于任何类型,方法属于特定的类型

  • 方法是与对象实例绑定的特殊函数。

  • 用于维护和展示对象自身的状态。对象是内敛的。普通函数则专注与算法流程,通过接受参数来完成特定的逻辑运算,并返回最终结果,方法是有关联状态的,函数通常是没有的。

  • 方法和函数定义语法区别在于前者实例接受参数,编译器以此确定方法所属的类型。在一些语言中尽管没有定义,但是函数使用了隐式的传递this实例参数。